/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package csp;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 *
 * @author Leandro
 */
public class ArcoConsistencia extends AlgortimoCSP {

    @Override
    public boolean resolver(CSP problema) {
       if(this.heuValor instanceof VMR){
        VMR valorAux= (VMR) this.heuValor;
        valorAux.setVerificador((ExpertoRestriccionesAntena)problema.getVerificadorRestricciones());
       }
       
       this.verificadorRestricciones = problema.getVerificadorRestricciones();
       this.medidasRendimiento.inicializarRuntime();
       boolean resultadoBusqueda=arcoConsistencia(problema);
       this.medidasRendimiento.finalizarRuntime();
       //Imprimir resultados?
       
       return resultadoBusqueda;
       
    }
    public ArcoConsistencia(HeuristicaValor heuValor, HeuristicaVariable heuVar){
        this.heuValor = heuValor;
        this.heuVar = heuVar;
        this.medidasRendimiento = new Rendimiento();
        
    }

    private boolean arcoConsistencia(CSP problema){
        this.medidasRendimiento.incRecursiones();
        if (problema.getVariablesNoAsignadas() == 0) {
           // return verificadorRestricciones.factible(problema);
            return true;
        } else {
            Variable variableActual = heuVar.getNextVariable(problema); //Heuristica selecciona variable
            if (variableActual != null) {
                List<Valor> valoresPosibles = variableActual.getValoresPosibles();
                List<Valor> valoresOrdenados=new ArrayList<Valor>( heuValor.ordenarValores(valoresPosibles,variableActual,problema.getDominio().size()));
                for (Valor valorAasignar : valoresOrdenados) {

                    variableActual.setAsignado(valorAasignar);
                    this.medidasRendimiento.incBackTracks();
                    //verificar si hace falta al debuggear
                    //problema.getVariables().get(problema.variablePorAsignar).setAsignado(valorAasignar);
                    if (verificadorRestricciones.factible(problema)) {

                        //Se remueve el valor asignado del dominio del resto de las variables
                        for (Variable var : problema.getVariables()) {
                            //if(var.getAsignado() == null){
                            var.getValoresPosibles().remove(valorAasignar);
                            //}
                        }
                        //System.out.println("Antes AC3");
                        if (AC3(problema)){
                           // System.out.println("Despues AC3");
                            if (arcoConsistencia(problema)) {
                                return true;
                            } else {
                           //devuelve los valores quitados a los dominios
                            for (Variable var : problema.getVariables()) {
                            //if(var.getAsignado() == null){
                            var.getValoresPosibles().add(valorAasignar);
                            //}
                        }
                        }

                        } else {
                           //devuelve los valores quitados a los dominios
                            for (Variable var : problema.getVariables()) {
                            //if(var.getAsignado() == null){
                            var.getValoresPosibles().add(valorAasignar);
                            //}
                        }
                        }
                    }
                }
               variableActual.setAsignado(null);
               
               return false;
            }
            return false;
        }
    }

    private boolean AC3(CSP problema){
        boolean rta=true;
        boolean hayElementos=false;
        int indice=0;
        List<Arco> cola=inicializarArcos(problema);
        List<Arco> colaAux=new ArrayList<Arco>(cola);
        //List<Arco> colaAux=new ArrayList<Arco>();
        //Iterator<Arco> iterador=colaAux.iterator();
        //for(Arco arco:cola){
        if(colaAux.size()>0){
        hayElementos=true;
        }

        Arco arco = null;
        while(hayElementos){
            arco=colaAux.get(indice);
            if(borrarValoresInconsistentes(arco.getXi(),arco.getXj())){
                for(Variable vecino: incidentes(arco.getXi(),cola/*,cola.indexOf(arco)*/)){
                    colaAux.add(new Arco(vecino, arco.getXi()));
                }
            }
            colaAux.remove(arco);
            //indice++;
            if(colaAux.size()<=0){
            hayElementos=false;
            break;
            }
        //}
        }
        for(Variable var:problema.getVariables()){
            if(var.getAsignado()==null){
                if(var.getValoresPosibles().isEmpty()){
                    rta=false;
                }
            }
        }
        return rta;
    }

    private List<Arco> inicializarArcos(CSP problema) {
        List<Arco> arcos=new ArrayList<Arco>();
        UbicacionAntena aux1;
        for(Variable X1:problema.getVariables()){
            aux1=(UbicacionAntena)X1;
            for(Variable visible:aux1.getVisibles()){
                Arco aux=new Arco(X1, visible);
                if(!estaEn(aux,arcos)){
                arcos.add(aux);
                }
            }
            for(Variable consecutiva:aux1.getConsecutivas()){
                Arco aux=new Arco(X1, consecutiva);
                if(!estaEn(aux,arcos)){
               arcos.add(aux);
                }
            }
        }
        return arcos;
    }

    private boolean borrarValoresInconsistentes(Variable xi, Variable xj) {
        boolean borrado=false;
        ExpertoRestriccionesAntena verificador=(ExpertoRestriccionesAntena)this.verificadorRestricciones;
        List<Valor> auxValoresI=new ArrayList<Valor>(xi.getValoresPosibles());
        for(Valor valorI:auxValoresI){
            if(!verificador.verificarValor(xi,valorI,xj)){
                xi.getValoresPosibles().remove(valorI);
                borrado=true;
            }
            
        }
        return borrado;
    }

    private List<Variable> incidentes(Variable xi,List<Arco> cola/*,int indice*/) {
        List<Variable> incidentes=new ArrayList<Variable>();
        for(Arco arco:cola){
            if(arco.getXj().equals(xi)){
                incidentes.add(arco.getXi());
            }
        }
        return incidentes;
    }

    private boolean estaEn(Arco aux, List<Arco> arcos) {
        boolean r=false;
        for(Arco a:arcos){
            if((a.getXi().equals(aux.getXi()))
                    &&(a.getXj().equals(aux.getXj()))){
            r=true;
            }
        }
        return r;
    }
}
